/*---------------------------------------------------------------------
 * File name: hal_timer.cpp
 *
 * Copyright (c) <2020-2022>, <ChenLong>
 *
 * All rights reserved.
 *
 * Author: ChenLong
 * Email: worldlong@foxmail.com
 *--------------------------------------------------------------------*/
#include "stm32f30x_tim.h"
#include "stm32f30x_rcc.h"
#include "hal_timer.h"
#include "hal_config.h"
#include "hal_system.h"
#include "stm32f30x_misc.h"
#if defined(USING_RT_THREAD)
#include "rtthread.h" 
#endif
/*
*/
static void (*irq_hook[11])(void *param) = {nullptr};
static void *irq_hook_param[11]={nullptr};
#if TIM1_ENABLE
static uint64_t timer1_last_micros = 0;
static uint64_t timer1_micros_high = 0;
#endif
#if TIM2_ENABLE
static uint64_t timer2_last_micros = 0;
static uint64_t timer2_micros_high = 0;
#endif
#if TIM3_ENABLE
static uint64_t timer3_last_micros = 0;
static uint64_t timer3_micros_high = 0;
#endif
#if TIM4_ENABLE
static uint64_t timer4_last_micros = 0;
static uint64_t timer4_micros_high = 0;
#endif
#if TIM6_ENABLE
static uint64_t timer6_last_micros = 0;
static uint64_t timer6_micros_high = 0;
#endif
#if TIM7_ENABLE
static uint64_t timer7_last_micros = 0;
static uint64_t timer7_micros_high = 0;
#endif
#if TIM8_ENABLE
static uint64_t timer8_last_micros = 0;
static uint64_t timer8_micros_high = 0;
#endif
#if TIM15_ENABLE
static uint64_t timer15_last_micros = 0;
static uint64_t timer15_micros_high = 0;
#endif
#if TIM16_ENABLE
static uint64_t timer16_last_micros = 0;
static uint64_t timer16_micros_high = 0;
#endif
#if TIM17_ENABLE
static uint64_t timer17_last_micros = 0;
static uint64_t timer17_micros_high = 0;
#endif
#if TIM20_ENABLE
static uint64_t timer20_last_micros = 0;
static uint64_t timer20_micros_high = 0;
#endif
/*
*/
bool HAL_Timer::init(uint64_t cfg0)
{
  uint8_t nvic_irqn = 0;
  uint8_t nvic_prio = 0;
  
  cfg = cfg0;
  uint32_t tmp = TIM_CFG_EXTRACT(cfg, PORT);
  if(tmp == 0) {  //No port
    return false;
  }
#if TIM1_ENABLE
  else if(tmp == TIM_PORT(1)) {
    port = (uint32_t)TIM1;
    _last_micros = &timer1_last_micros;
    _micros_high = &timer1_micros_high;
    counter_bits = 16;
    _index = 0;
    nvic_irqn = TIM1_UP_TIM16_IRQn;
    nvic_prio = NVIC_IRQ_TIM1_PRIORITY;
  } 
#endif
#if TIM2_ENABLE
  else if(tmp == TIM_PORT(2)) {
    port = (uint32_t)TIM2;
    _last_micros = &timer2_last_micros;
    _micros_high = &timer2_micros_high;
    counter_bits = 32;
    _index = 1;
    nvic_irqn = TIM2_IRQn;
    nvic_prio = NVIC_IRQ_TIM2_PRIORITY;
  }
#endif
#if TIM3_ENABLE
  else if(tmp == TIM_PORT(3)) {
    port = (uint32_t)TIM3;
    _last_micros = &timer3_last_micros;
    _micros_high = &timer3_micros_high;
    counter_bits = 16;
    _index = 2;
    nvic_irqn = TIM3_IRQn;
    nvic_prio = NVIC_IRQ_TIM3_PRIORITY;
  }
#endif
#if TIM4_ENABLE
  else if(tmp == TIM_PORT(4)) {
    port = (uint32_t)TIM4;
    _last_micros = &timer4_last_micros;
    _micros_high = &timer4_micros_high;
    counter_bits = 16;
    _index = 3;
    nvic_irqn = TIM4_IRQn;
    nvic_prio = NVIC_IRQ_TIM4_PRIORITY;
  }
#endif
#if TIM6_ENABLE  
  else if(tmp == TIM_PORT(6)) {
    port = (uint32_t)TIM6;
    _last_micros = &timer6_last_micros;
    _micros_high = &timer6_micros_high;
    counter_bits = 16;
    _index = 4;
    nvic_irqn = TIM6_DAC_IRQn;
    nvic_prio = NVIC_IRQ_TIM6_PRIORITY;
  }
#endif
#if TIM7_ENABLE  
  else if(tmp == TIM_PORT(7)) {
    port = (uint32_t)TIM7;
    _last_micros = &timer7_last_micros;
    _micros_high = &timer7_micros_high;
    counter_bits = 16;
    _index = 5;
    nvic_irqn = TIM7_IRQn;
    nvic_prio = NVIC_IRQ_TIM7_PRIORITY;
  }
#endif
#if TIM8_ENABLE  
  else if(tmp == TIM_PORT(8)) {
    port = (uint32_t)TIM8;
    _last_micros = &timer8_last_micros;
    _micros_high = &timer8_micros_high;
    counter_bits = 16;
    _index = 6;
    nvic_irqn = TIM8_UP_IRQn;
    nvic_prio = NVIC_IRQ_TIM8_PRIORITY;
  }
#endif
#if TIM15_ENABLE  
  else if(tmp == TIM_PORT(15)) {
    port = (uint32_t)TIM15;
    _last_micros = &timer15_last_micros;
    _micros_high = &timer15_micros_high;
    counter_bits = 16;
    _index = 7;
    nvic_irqn = TIM1_BRK_TIM15_IRQn;
    nvic_prio = NVIC_IRQ_TIM15_PRIORITY;
  }
#endif
#if TIM16_ENABLE  
  else if(tmp == TIM_PORT(16) {
    port = (uint32_t)TIM16;
    _last_micros = &timer16_last_micros;
    _micros_high = &timer16_micros_high;
    counter_bits = 16;
    _index = 8;
    nvic_irqn = TIM1_UP_TIM16_IRQn;
    nvic_prio = NVIC_IRQ_TIM16_PRIORITY;
  }
#endif
#if TIM17_ENABLE  
  else if(tmp == TIM_PORT(17)) {
    port = (uint32_t)TIM17;
    _last_micros = &timer17_last_micros;
    _micros_high = &timer17_micros_high;
    counter_bits = 16;
    _index = 9;
    nvic_irqn = TIM1_UP_TIM17_IRQn;
    nvic_prio = NVIC_IRQ_TIM17_PRIORITY;
  }
#endif
#if TIM20_ENABLE  
  else if(tmp == TIM_PORT(20)) {
    port = (uint32_t)TIM20;
    _last_micros = &timer20_last_micros;
    _micros_high = &timer20_micros_high;
    counter_bits = 16;
    _index = 10;
    nvic_irqn = TIM20_UP_IRQn;
    nvic_prio = NVIC_IRQ_TIM20_PRIORITY;
  }
#endif
  else if(port == 0){
    return false;
  }
          
  if(!config(cfg)) {
    return false;
  }
  
  NVIC_InitTypeDef   NVIC_InitStructure;
  TIM_TypeDef *TIMx = (TIM_TypeDef *)port;
  tmp = TIM_CFG_EXTRACT(cfg, INT);
  if(tmp == TIM_INT(ENABLE) && nvic_irqn > 0) {
    TIM_ClearITPendingBit(TIMx, TIM_IT_Update);
    TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE);
    
    NVIC_InitStructure.NVIC_IRQChannel = nvic_irqn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = nvic_prio;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
  }
  
  return true;
}
/*
*/
bool HAL_Timer::config(uint64_t cfg0)
{  
  cfg &= ~TIM_CFG_MASK;
  cfg |= cfg0 & TIM_CFG_MASK;
  
  clock = (uint32_t)TIM_CFG_EXTRACT(cfg, CLOCK);
  freq = (uint32_t)TIM_CFG_EXTRACT(cfg, FREQ);
  
  if(freq >= clock) {
    return false;
  }
  
  if(freq > 0) {
    period = clock / freq;
    if(counter_bits == 16 && period > 0x10000) {
      return false;
    }
  }
  
  //Attention, timer clock input must =SystemCoreClock
  uint32_t prescalar = SystemCoreClock / clock;  
  if(prescalar > 0x10000) {
    return false;
  }
  
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  TIM_TimeBaseStructure.TIM_Period = period - 1;
  TIM_TimeBaseStructure.TIM_Prescaler = (uint16_t)prescalar - 1;
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  
  uint32_t tmp = TIM_CFG_EXTRACT(cfg, CTM);
  if(tmp == TIM_CTM(UP)) {
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  } else if(tmp == TIM_CTM(DOWN)) {
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Down;
  } else if(tmp == TIM_CTM(CA1)) {
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_CenterAligned1;
  } else if(tmp == TIM_CTM(CA2)) {
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_CenterAligned2;
  } else if(tmp == TIM_CTM(CA3)) {
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_CenterAligned3;
  } else {
    return false;
  }
  
  TIM_TypeDef *TIMx = (TIM_TypeDef *)port;
  TIM_Cmd(TIMx, DISABLE);
  TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStructure);
  TIM_ARRPreloadConfig(TIMx, ENABLE);
  TIMx->CNT = 0;
  TIM_Cmd(TIMx, ENABLE);
  
  return true;
}
/*
*/
uint64_t HAL_Timer::micros()
{
  if(!port) {
    return 0;
  }
  
  uint32_t sr = enter_critical();
  
  uint64_t micros = *_micros_high + (uint64_t)(((TIM_TypeDef*)port)->CNT);
  /* If TIMx->CNT had been overflow, but the interrupt function
     have not be handled, so micros will be small than last_micros,
     we add (1ll<<counter_bits) to micros manually.*/
  if(micros < *_last_micros) {
    micros += (1ll<<counter_bits);
  }
  *_last_micros = micros;
  
  exit_critical(sr);
  
  if(clock == 1000000) {
    return micros;
  } else if(clock > 1000000) {
    return micros / (clock / 1000000);
  } else {
    return micros * (1000000 / clock);
  }
}
/*
*/
void HAL_Timer::attach_irq(void(*fun)(void *param), void *param)
{
  irq_hook[_index] = fun;
  irq_hook_param[_index] = param;
  if(irq_hook[0]) {}        //just use to remove the Warning[Pe550]
  if(irq_hook_param[0]) {}  //just use to remove the Warning[Pe550]
}

/*************************************************
              IRQHandler
**************************************************/
#ifdef __cplusplus
 extern "C" {
#endif
//TIM1
#if TIM1_ENABLE || TIM16_ENABLE
void TIM1_UP_TIM16_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
#if TIM1_ENABLE
  if (TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET)
  {
    TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
    timer1_micros_high += 1ll<<16;
    if(irq_hook[0] != nullptr) {
      irq_hook[0](irq_hook_param[0]);
    }
  }
#endif
#if TIM16_ENABLE
  if (TIM_GetITStatus(TIM16, TIM_IT_Update) != RESET)
  {
    TIM_ClearITPendingBit(TIM16, TIM_IT_Update);
    timer16_micros_high += 1ll<<16;
    if(irq_hook[8] != nullptr) {
      irq_hook[8](irq_hook_param[8]);
    }
  }
#endif
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif
//TIM2
#if TIM2_ENABLE
void TIM2_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
  {
    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    timer2_micros_high += 1ll<<32;
    if(irq_hook[1] != nullptr) {
      irq_hook[1](irq_hook_param[1]);
    }
  }
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif
//TIM3
#if TIM3_ENABLE
void TIM3_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
  {
    TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
    timer3_micros_high += 1ll<<16;
    if(irq_hook[2] != nullptr) {
      irq_hook[2](irq_hook_param[2]);
    }
  }
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif
//TIM4
#if TIM4_ENABLE
void TIM4_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
  {
    TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
    timer4_micros_high += 1ll<<16;
    if(irq_hook[3] != nullptr) {
      irq_hook[3](irq_hook_param[3]);
    }
  }
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif
//TIM6
#if TIM6_ENABLE
void TIM6_DAC_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)
  {
    TIM_ClearITPendingBit(TIM6, TIM_IT_Update);
    timer6_micros_high += 1ll<<16;
    if(irq_hook[4] != nullptr) {
      irq_hook[4](irq_hook_param[4]);
    }
  }
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif
//TIM7
#if TIM7_ENABLE
void TIM7_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET)
  {
    TIM_ClearITPendingBit(TIM7, TIM_IT_Update);
    timer7_micros_high += 1ll<<16;
    if(irq_hook[5] != nullptr) {
      irq_hook[5](irq_hook_param[5]);
    }
  }
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif
//TIM8
#if TIM8_ENABLE
void TIM8_UP_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if (TIM_GetITStatus(TIM8, TIM_IT_Update) != RESET)
  {
    TIM_ClearITPendingBit(TIM8, TIM_IT_Update);
    timer8_micros_high += 1ll<<16;
    if(irq_hook[6] != nullptr) {
      irq_hook[6](irq_hook_param[6]);
    }
  }
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif
//TIM15
#if TIM15_ENABLE
void TIM1_BRK_TIM15_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if (TIM_GetITStatus(TIM15, TIM_IT_Update) != RESET)
  {
    TIM_ClearITPendingBit(TIM15, TIM_IT_Update);
    timer15_micros_high += 1ll<<16;
    if(irq_hook[7] != nullptr) {
      irq_hook[7](irq_hook_param[7]);
    }
  }
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif
//TIM17
#if TIM17_ENABLE
void TIM1_TRG_COM_TIM17_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if (TIM_GetITStatus(TIM17, TIM_IT_Update) != RESET)
  {
    TIM_ClearITPendingBit(TIM17, TIM_IT_Update);
    timer17_micros_high += 1ll<<16;
    if(irq_hook[9] != nullptr) {
      irq_hook[9](irq_hook_param[9]);
    }
  }
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif
//TIM20
#if TIM20_ENABLE
void TIM20_UP_IRQHandler(void)
{
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if (TIM_GetITStatus(TIM20, TIM_IT_Update) != RESET)
  {
    TIM_ClearITPendingBit(TIM20, TIM_IT_Update);
    timer20_micros_high += 1ll<<16;
    if(irq_hook[10] != nullptr) {
      irq_hook[10](irq_hook_param[10]);
    }
  }
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
#endif
/*
*/
#ifdef __cplusplus
}
#endif




